using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;


namespace Mpi.Common
{
    public class SimpleOboReader
    {
        private Stream currentStream;
        public void Open(string @path)
        {
            currentStream = File.Open(path, FileMode.Open, FileAccess.Read);
        }

        public void Read()
        {
            Read(this.currentStream);
        }

        public void Read(Stream stream)
        {
            using (TextReader tx = new StreamReader(stream))
            {
                try
                {
                    Read(tx);
                }
                catch (Exception)
                {
                    throw;
                }
                finally
                {
                    tx.Close();
                }
            }
        }

        public void Read(string oboText)
        {
            using (TextReader tx = new StringReader(oboText))
            {
                try
                {
                    Read(tx);
                }
                catch (Exception)
                {
                    throw;
                }
                finally
                {
                    tx.Close();
                }
            }
        }

        public void Read(string[] attributeSubscription)
        {
            Read(this.currentStream, attributeSubscription);
        }

        public void Read(Stream stream, string[] attributeSubscription)
        {
            TextReader tx = new StreamReader(stream);
            Read(tx, attributeSubscription);
        }

        public void Read(TextReader txr)
        {
            Read(txr, null);
            //int lineNumber = 0;
            //while (txr.Peek() > -1)
            //{
            //    string line = txr.ReadLine();
            //    if (line.TrimStart(" ".ToCharArray()).StartsWith("[Term]"))
            //        OnNewStanza("Term", lineNumber);
            //    else
            //    {
            //        string[] split = line.Split(":".ToCharArray(), 2);
            //        if (split.Length > 1)
            //        {
            //            OnNewAttribute(split[0].Trim(), split[1].Trim(), lineNumber);
            //        }
            //    }

            //    lineNumber++;
            //}
            //txr.Close();
        }

        public void Read(TextReader txr, string[] attributeSubscription)
        {
            List<string> filter = null;
            if (attributeSubscription != null)
                filter = new List<string>(attributeSubscription);
            int lineNumber = 0;
            bool fristStanzaFound = false;
            while (txr.Peek() > -1)
            {
                string line = txr.ReadLine();

                if (CheckForNewStanza(line, lineNumber))
                {
                    fristStanzaFound = true;

                }

                else
                {
                    if (fristStanzaFound)
                        CheckForTagValuePair(filter, lineNumber, line); //with filter
                    else
                        CheckForTagValuePair(null, lineNumber, line); //without filter for header tags

                }

                lineNumber++;
            }
            txr.Close();
        }

        private void CheckForTagValuePair(List<string> filter, int lineNumber, string line)
        {
            Match m = regexTagValPair.Match(line);
            if (m.Success)
            {
                string[] split = line.Split(":".ToCharArray(), 2);
                if (split.Length > 1)
                {

                    if (filter == null || filter.Contains(split[0].Trim()))
                        OnNewTagValuePair(split[0].Trim(), split[1].Trim(), lineNumber);
                }
            }
        }

        private bool CheckForNewStanza(string line, int lineNumber)
        {
            Match stanza = regexStanza.Match(line);
            if (stanza.Success)
            {
                string stanzaType = "Unknown";

                Match m = regexTerm.Match(line);
                string name = stanza.Value.Trim("[]".ToCharArray());

                if (m.Success)
                {
                    stanzaType = m.Value.Trim("[]".ToCharArray());
                    name = stanzaType;
                }
                m = regexTypedef.Match(line);
                if (m.Success)
                {
                    stanzaType = m.Value.Trim("[]".ToCharArray());
                    name = stanzaType;
                }


                OnNewStanza(stanzaType, name, lineNumber);
                return true;
            }
            return false;
        }

        Regex regexStanza;
        Regex regexTerm;
        Regex regexTypedef;
        Regex regexTagValPair;

        #region constructors, destructors
        public SimpleOboReader()
        {
            regexStanza = new Regex(@"\[\w+\]");
            regexTerm = new Regex(@"\[Term\]");
            regexTypedef = new Regex(@"\[Typedef\]");
            regexTagValPair = new Regex(@"(?<value>.+:.+)");
        }

        ~SimpleOboReader()
        {
            if (currentStream != null)
            {
                currentStream.Close();
            }
        }
        #endregion constructors, destructors

        public event EventHandler<NewStanzaEventArgs> NewStanza;
        protected virtual void OnNewStanza(string stanzaType, string name, int line)
        {
            if (NewStanza != null)
            {
                StanzaType st = (StanzaType)Enum.Parse(typeof(StanzaType), stanzaType, true);
                NewStanza(this, new NewStanzaEventArgs(st, name, line));
            }
        }

        public event EventHandler<NewAttributeEventArgs> NewTagValuePair;
        protected virtual void OnNewTagValuePair(string tag, string tagVal, int line)
        {
            if (NewTagValuePair != null)
            {
                NewTagValuePair(this, new NewAttributeEventArgs(tag, tagVal, line));
            }
        }
    }

    public enum StanzaType
    {
        Term,
        TypeDef,
        Unknown
    }

    public class NewStanzaEventArgs : LineEventArgs
    {
        public StanzaType StanzaType { get; private set; }
        public string Name { get; private set; }

        public NewStanzaEventArgs(StanzaType type, string name, int line)
            : base(line)
        {
            Name = name;
            StanzaType = type;
        }
    }

    public abstract class LineEventArgs : EventArgs
    {
        public int Line { get; protected set; }

        public LineEventArgs(int line)
        {
            this.Line = line;
        }
    }

    public class NewAttributeEventArgs : LineEventArgs
    {
        public string Attribute { get; private set; }
        public string AttributeValue { get; private set; }

        public NewAttributeEventArgs(string attr, string attrVal, int line)
            : base(line)
        {
            Attribute = attr;
            AttributeValue = attrVal;
        }
    }
}
